package com.oh.core.server.handler;

import com.oh.core.common.base.MessageService;
import com.oh.core.common.cache.ChannelStore;
import com.oh.core.common.constant.CommandEnum;
import com.oh.core.common.constant.ConfigConst;
import com.oh.core.common.utils.MessageUtils;
import com.oh.core.common.utils.ServiceRouteFactory;
import com.oh.core.entity.MessageEntity;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;

/**
 * 读取到客户端发来的消息
 */
@ChannelHandler.Sharable
@Component
public class ServerHandler extends SimpleChannelInboundHandler<byte []> {

    private final Logger log = LoggerFactory.getLogger(ServerHandler.class);
    /**
     * 管理全局的channel
     * GlobalEventExecutor.INSTANCE  全局事件监听器
     * 一旦将channel 加入 ChannelGroup 就不要用手动去
     * 管理channel的连接失效后移除操作，他会自己移除
     */
    public static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 为了实现私聊功能，这里key存储用户的唯一标识，
     * 当然 这个集合也需要自己去维护 用户的上下线 不能像 ChannelGroup那样自己去维护
     */
    // private static Map<String,Channel> allChannel = ChannelStore.getAllChannel();

    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    /**
     * 同 channelRead 方法
     * 读取到客户端发来的消息
     * @param ctx
     * @param bytes
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, byte[] bytes) throws Exception {
        MessageEntity messageEntity = new MessageEntity(bytes);
        log.debug("消息头 " + messageEntity.getHeadInfo().toJson());
        Optional<MessageService> optional = ServiceRouteFactory.getService(messageEntity.getCommand());
        if(optional.isPresent()){
            MessageService messageService = optional.get();
            // 处理消息
            messageService.handle(ctx, messageEntity);
        }else{
            log.error("未获取到相关服务，指令【{}】无效！{}", messageEntity.getCommand(), sdf.format(new Date()));
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        String channelId = channel.id().asLongText();
        ChannelStore.setChannel(channelId, channel);
        clients.add(channel);
        log.info("连接数" + ChannelStore.getAllChannel().size());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端断开链接");
        clients.remove(ctx.channel());
        String channelId = ctx.channel().id().asLongText();
        ChannelStore.removeChannel(channelId);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        String msg = this.getClass().getName() + "发生异常:"+cause.getMessage();
        log.error(msg);
//        cause.printStackTrace();
        Channel channel = ctx.channel();
        String channelId = channel.id().asLongText();
        ChannelStore.removeChannel(channelId);
        channel.writeAndFlush(MessageUtils.splicingMsg(CommandEnum.MESSAGE_REQUEST.getCommand(), ConfigConst.MAGIC_NUMBER, msg.getBytes()));
        channel.close();
        clients.remove(ctx.channel());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        log.info("执行了channelActive，channelActive = " + ctx.name());
        ctx.fireChannelActive();
    }

    /**
     * channel连接状态断开后触发
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        InetSocketAddress ipSocket = (InetSocketAddress)channel.remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        int port = ipSocket.getPort();
        String msg = sdf.format(new Date()) +" 用户：" + channel.id().asLongText() + " IP端口：" + clientIp + ":" + port +" 下线 ";
        log.info(msg);
        // 通知所有客户端
        MessageUtils.sendMsgToAll(msg.getBytes());
    }
}
